﻿using System;
using Moq;
using NUnit.Framework;
using Shinkansen.Configuration;
using Shinkansen.Support;

namespace Shinkansen.Test
{
    [TestFixture]
    public class CryptographyTest
    {
        private Mock<ICryptoKeyProvider> _cryptoProvider;
        private Cryptography _crypto;

        [SetUp]
        public void Setup ()
        {
            _cryptoProvider = new Mock<ICryptoKeyProvider>();
            // NOTE: In production, a different key/IV set should be generated
            _cryptoProvider.SetupGet (m => m.Key).Returns ("EF5DE97E40AFCCA4C6F30F3DC1826F96C24819CD8152F8A7F9D3E85BC0A84249");
            _cryptoProvider.SetupGet (m => m.IV).Returns ("5564DC65E528EFA52EAA85849E9E68EA73DFEE39E74FA53EC33D78C2F67313D282CCBD26EFBA3DE837477B680A5720543A1D82121D84F6EEB6F0653495B8086E");
         
            CryptoProviderBuilder.Current.SetCryptoProvider (_cryptoProvider.Object);
            _crypto = new Cryptography ();
        }

        [TearDown]
        public void Teardown ()
        {
            CryptoProviderBuilder.Current.Reset ();

            _cryptoProvider = null;
            _crypto = null;
        }

        [Test]
        public void Reads_key_and_iv ()
        {
            _crypto.Encrypt ("Lorem ipsum dolor sit amet, consectetur adipiscing elit");

            _cryptoProvider.VerifyGet (m => m.Key, Times.Exactly (1));
            _cryptoProvider.VerifyGet (m => m.IV, Times.Exactly (1));
        }

        [Test]
        public void Can_encrypt_arbitrary_text ()
        {
            byte[] encryptedResult = _crypto.Encrypt ("Lorem ipsum dolor sit amet, consectetur adipiscing elit");

            Assert.IsNotNull (encryptedResult);
            Assert.IsNotEmpty (encryptedResult);
        }

        [Test]
        public void Can_decrypt_arbitrary_text ()
        {
            string  text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit";
            byte[]  encryptedResult = _crypto.Encrypt (text);
            string  plainText = _crypto.Decrypt (encryptedResult);

            Assert.IsNotNullOrEmpty (plainText);
            Assert.AreEqual (text, plainText);
            Assert.AreNotSame (text, plainText);
        }

        [Test]
        [ExpectedException (typeof (ArgumentNullException))]
        public void Should_not_accept_null_for_plan_text ()
        {
            _crypto.Encrypt (null);
        }

        [Test]
        [ExpectedException (typeof (ArgumentNullException))]
        public void Should_not_accept_null_for_encrypted_text ()
        {
            _crypto.Decrypt (null);
        }

        [Test]
        public void Can_generate_md5_from_text ()
        {
            string md5 = _crypto.GenerateMD5Hash ("Lorem ipsum dolor sit amet, consectetur adipiscing elit");
            Assert.IsNotNullOrEmpty (md5);
        }

        [Test]
        public void Can_generate_md5_from_number ()
        {
            string md5 = _crypto.GenerateMD5Hash (DateTime.Now.Ticks);
            Assert.IsNotNullOrEmpty (md5);
        }

        [Test]
        [ExpectedException (typeof (ArgumentNullException))]
        public void Cannot_md5_null ()
        {
            _crypto.GenerateMD5Hash (null);
        }
    }
}
